"""
标记价格模块
获取合约的标记价格，用于防止市场操纵
"""
import time
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from loguru import logger

try:
    import okx.PublicData as PublicData
except ImportError:
    logger.warning("okx SDK未安装，请运行: pip install okx")
    PublicData = None


class MarkPriceManager:
    """标记价格管理器"""
    
    # 限速配置：10次/2s
    RATE_LIMIT_REQUESTS = 10
    RATE_LIMIT_WINDOW = 2.0  # 秒
    
    def __init__(self, sandbox: bool = True):
        """
        初始化标记价格管理器
        
        Args:
            sandbox: 是否使用模拟盘，True=模拟盘(1)，False=实盘(0)
        """
        if PublicData is None:
            raise ImportError("okx SDK未安装，请运行: pip install okx")
        
        self.sandbox = sandbox
        flag = "1" if sandbox else "0"
        
        # 初始化OKX公共数据API
        self.public_api = PublicData.PublicAPI(flag=flag)
        
        # 限速控制 - 按产品ID分别限速
        self.request_times: Dict[str, List[float]] = {}  # inst_id -> 请求时间戳列表
        
        # 数据缓存
        self.mark_price_cache: Dict[str, List[Dict]] = {}  # cache_key -> mark_price_data
        self.cache_expire_time: Dict[str, datetime] = {}  # cache_key -> expire_time
        self.cache_duration = timedelta(seconds=30)  # 缓存30秒
        
        logger.info(f"标记价格管理器已初始化 (模式: {'模拟盘' if sandbox else '实盘'})")
    
    def _check_rate_limit(self, key: str):
        """
        检查并执行限速
        限速规则：10次/2s (IP + Instrument ID)
        
        Args:
            key: 限速键（产品ID或产品类型）
        """
        now = time.time()
        
        # 初始化该键的请求记录
        if key not in self.request_times:
            self.request_times[key] = []
        
        # 移除2秒前的请求记录
        self.request_times[key] = [t for t in self.request_times[key]
                                   if now - t < self.RATE_LIMIT_WINDOW]
        
        # 如果达到限速上限，等待
        if len(self.request_times[key]) >= self.RATE_LIMIT_REQUESTS:
            oldest_request = self.request_times[key][0]
            wait_time = self.RATE_LIMIT_WINDOW - (now - oldest_request)
            
            if wait_time > 0:
                logger.debug(f"触发限速 ({key})，等待 {wait_time:.2f} 秒")
                time.sleep(wait_time)
                # 重新计算
                now = time.time()
                self.request_times[key] = [t for t in self.request_times[key]
                                          if now - t < self.RATE_LIMIT_WINDOW]
        
        # 记录本次请求
        self.request_times[key].append(now)
    
    def get_mark_price(self, inst_type: str,
                      uly: Optional[str] = None,
                      inst_family: Optional[str] = None,
                      inst_id: Optional[str] = None,
                      use_cache: bool = True) -> List[Dict]:
        """
        获取标记价格
        
        Args:
            inst_type: 产品类型
                - MARGIN: 币币杠杆
                - SWAP: 永续合约
                - FUTURES: 交割合约
                - OPTION: 期权
            uly: 标的指数，如 BTC-USD
            inst_family: 交易品种，如 BTC-USD
            inst_id: 产品ID，如 BTC-USD-SWAP
            use_cache: 是否使用缓存
            
        Returns:
            标记价格信息列表，每个产品包含以下字段：
            - instType: 产品类型
            - instId: 产品ID
            - markPx: 标记价格
            - ts: 数据返回时间（毫秒时间戳）
        """
        # 生成缓存键
        cache_key = f"{inst_type}_{uly}_{inst_family}_{inst_id}"
        
        # 检查缓存
        if use_cache and cache_key in self.mark_price_cache:
            expire_time = self.cache_expire_time.get(cache_key)
            if expire_time and datetime.now() < expire_time:
                logger.debug(f"从缓存获取标记价格: {cache_key}")
                return self.mark_price_cache[cache_key]
        
        # 执行限速检查（使用inst_id或inst_type作为限速键）
        rate_limit_key = inst_id if inst_id else inst_type
        self._check_rate_limit(rate_limit_key)
        
        try:
            # 构建请求参数
            params = {"instType": inst_type}
            if uly:
                params["uly"] = uly
            if inst_family:
                params["instFamily"] = inst_family
            if inst_id:
                params["instId"] = inst_id
            
            logger.info(f"获取标记价格: {params}")
            result = self.public_api.get_mark_price(**params)
            
            # 检查响应
            if result.get("code") != "0":
                error_msg = result.get("msg", "未知错误")
                logger.error(f"获取标记价格失败: {error_msg}")
                raise RuntimeError(f"API错误: {error_msg}")
            
            # 提取数据
            data_list = result.get("data", [])
            logger.info(f"获取标记价格成功: {len(data_list)} 个产品")
            
            # 更新缓存
            if use_cache:
                self.mark_price_cache[cache_key] = data_list
                self.cache_expire_time[cache_key] = datetime.now() + self.cache_duration
            
            return data_list
            
        except Exception as e:
            logger.error(f"获取标记价格异常: {e}")
            raise
    
    def get_mark_price_by_id(self, inst_id: str, use_cache: bool = True) -> Optional[Dict]:
        """
        根据产品ID获取单个产品的标记价格
        
        Args:
            inst_id: 产品ID，如 BTC-USD-SWAP
            use_cache: 是否使用缓存
            
        Returns:
            标记价格信息字典，如果不存在返回None
        """
        # 从inst_id推断产品类型
        if "-SWAP" in inst_id:
            inst_type = "SWAP"
        elif "-" in inst_id and inst_id.count("-") >= 2:
            parts = inst_id.split("-")
            if len(parts) >= 3 and len(parts[2]) == 6 and parts[2].isdigit():
                inst_type = "FUTURES"
            else:
                inst_type = "OPTION"
        else:
            inst_type = "MARGIN"
        
        try:
            data_list = self.get_mark_price(
                inst_type=inst_type,
                inst_id=inst_id,
                use_cache=use_cache
            )
            
            if data_list:
                return data_list[0]
            return None
            
        except Exception as e:
            logger.error(f"获取标记价格失败: {inst_id}, {e}")
            return None
    
    def get_mark_price_details(self, inst_id: str) -> Dict:
        """
        获取标记价格详细信息（包含格式化数据）
        
        Args:
            inst_id: 产品ID
            
        Returns:
            详细信息字典：
            - inst_id: 产品ID
            - inst_type: 产品类型
            - mark_price: 标记价格
            - timestamp: 数据时间（格式化）
            - timestamp_ms: 数据时间（毫秒时间戳）
        """
        mark_price_data = self.get_mark_price_by_id(inst_id)
        
        if not mark_price_data:
            raise ValueError(f"无法获取标记价格: {inst_id}")
        
        # 提取数据
        mark_price = float(mark_price_data.get("markPx", 0))
        ts = int(mark_price_data.get("ts", 0))
        
        # 格式化时间
        timestamp_str = datetime.fromtimestamp(ts / 1000).strftime("%Y-%m-%d %H:%M:%S")
        
        return {
            "inst_id": inst_id,
            "inst_type": mark_price_data.get("instType"),
            "mark_price": mark_price,
            "timestamp": timestamp_str,
            "timestamp_ms": ts,
        }
    
    def get_swap_mark_prices(self, uly: Optional[str] = None,
                            use_cache: bool = True) -> List[Dict]:
        """
        获取永续合约标记价格
        
        Args:
            uly: 标的指数，如 BTC-USD
            use_cache: 是否使用缓存
            
        Returns:
            永续合约标记价格列表
        """
        return self.get_mark_price("SWAP", uly=uly, use_cache=use_cache)
    
    def get_futures_mark_prices(self, uly: Optional[str] = None,
                               use_cache: bool = True) -> List[Dict]:
        """
        获取交割合约标记价格
        
        Args:
            uly: 标的指数，如 BTC-USD
            use_cache: 是否使用缓存
            
        Returns:
            交割合约标记价格列表
        """
        return self.get_mark_price("FUTURES", uly=uly, use_cache=use_cache)
    
    def get_option_mark_prices(self, uly: Optional[str] = None,
                              use_cache: bool = True) -> List[Dict]:
        """
        获取期权标记价格
        
        Args:
            uly: 标的指数，如 BTC-USD
            use_cache: 是否使用缓存
            
        Returns:
            期权标记价格列表
        """
        return self.get_mark_price("OPTION", uly=uly, use_cache=use_cache)
    
    def get_multiple_mark_prices(self, inst_ids: List[str]) -> Dict[str, Dict]:
        """
        批量获取多个产品的标记价格
        
        Args:
            inst_ids: 产品ID列表
            
        Returns:
            字典，key为产品ID，value为标记价格信息
        """
        results = {}
        
        for inst_id in inst_ids:
            try:
                mark_price_data = self.get_mark_price_by_id(inst_id)
                results[inst_id] = mark_price_data
            except Exception as e:
                logger.error(f"获取 {inst_id} 标记价格失败: {e}")
                results[inst_id] = None
        
        return results
    
    def compare_mark_prices(self, inst_ids: List[str]) -> List[Dict]:
        """
        比较多个产品的标记价格
        
        Args:
            inst_ids: 产品ID列表
            
        Returns:
            排序后的标记价格列表（按价格从高到低）
        """
        price_list = []
        
        for inst_id in inst_ids:
            try:
                details = self.get_mark_price_details(inst_id)
                price_list.append(details)
            except Exception as e:
                logger.error(f"获取 {inst_id} 标记价格失败: {e}")
        
        # 按标记价格排序（从高到低）
        price_list.sort(key=lambda x: x["mark_price"], reverse=True)
        
        return price_list
    
    def clear_cache(self, cache_key: Optional[str] = None):
        """
        清除缓存
        
        Args:
            cache_key: 缓存键，如果为None则清除所有缓存
        """
        if cache_key:
            if cache_key in self.mark_price_cache:
                del self.mark_price_cache[cache_key]
            if cache_key in self.cache_expire_time:
                del self.cache_expire_time[cache_key]
            logger.info(f"已清除 {cache_key} 的缓存")
        else:
            self.mark_price_cache.clear()
            self.cache_expire_time.clear()
            logger.info("已清除所有标记价格缓存")
    
    def get_cache_stats(self) -> Dict:
        """
        获取缓存统计信息
        
        Returns:
            缓存统计字典
        """
        total_cached = len(self.mark_price_cache)
        valid_cached = sum(1 for key in self.mark_price_cache.keys()
                          if key in self.cache_expire_time and
                          datetime.now() < self.cache_expire_time[key])
        
        return {
            "total_cached": total_cached,
            "valid_cached": valid_cached,
            "expired_cached": total_cached - valid_cached,
            "cache_duration_seconds": self.cache_duration.total_seconds(),
        }


# 便捷函数
def get_mark_price(inst_type: str, sandbox: bool = True, **kwargs) -> List[Dict]:
    """
    便捷函数：获取标记价格
    
    Args:
        inst_type: 产品类型 (MARGIN/SWAP/FUTURES/OPTION)
        sandbox: 是否使用模拟盘
        **kwargs: 其他参数传递给get_mark_price
        
    Returns:
        标记价格信息列表
    """
    manager = MarkPriceManager(sandbox=sandbox)
    return manager.get_mark_price(inst_type, **kwargs)


def get_mark_price_details(inst_id: str, sandbox: bool = True) -> Dict:
    """
    便捷函数：获取标记价格详细信息
    
    Args:
        inst_id: 产品ID
        sandbox: 是否使用模拟盘
        
    Returns:
        详细信息字典
    """
    manager = MarkPriceManager(sandbox=sandbox)
    return manager.get_mark_price_details(inst_id)


def compare_mark_prices(inst_ids: List[str], sandbox: bool = True) -> List[Dict]:
    """
    便捷函数：比较多个产品的标记价格
    
    Args:
        inst_ids: 产品ID列表
        sandbox: 是否使用模拟盘
        
    Returns:
        排序后的标记价格列表
    """
    manager = MarkPriceManager(sandbox=sandbox)
    return manager.compare_mark_prices(inst_ids)
